HTTP cache笔记
Last-Modified 与 If-Modified-Since
在浏览器第一次请求某 url 获得的返回状态是 200 的响应头中,获取 Last-Modified
字段,这个字段标识此文件在服务器端的最后被修改时间,格式类似于:
Last-Modified Thu, 15 Oct 2015 02:45:17 GMT
然后在浏览器第二次请求此 url 时,浏览器会根据之前获得的 Last-Modified
向服务器传送 If-Modified-Since
请求头,询问文件是否有被修改过:
If-Modified-Since Thu, 15 Oct 2015 02:45:17 GMT
如果服务器端资源没有变化,则请求返回 304 以达到缓存效果。
如果服务器端资源发生改变或者重启服务器,则重新响应资源,返回 200,与第一次相同。
如果
If-Modified-Since
的时间比服务器当前时间还晚,则会被认定为非法请求。
ETag 与 If-None-Match
HTTP 协议定义 ETag
为 被请求变量的实体标记,类似于一个资源的 hash 值,用于对 url 进行标记。这个值完全取决于服务器,并在响应头中返回浏览器。格式类似于:
Etag 9a932760982d29a83836cbd469bb14e7
第二次请求相同的数据时,浏览器的请求头中带上 If-None-Match
字段,如果服务器端资源没有变化,则请求返回 304。
If-None-Match 9a932760982d29a83836cbd469bb14e7
以上两个方式虽然完成了缓存文件功能,但是每次请求 url 时还是会向服务器发送一个请求头 If-Modified-Since
和 If-None-Match
。使用一些别的策略,可以省略这个请求过程。
Expires
Expires
是存在于响应头中的字段,它用一个绝对的时间点来指定文件的有效时间,(没有设置其他缓存控制的字段情况下)在有效时间内直接使用本地缓存文件。格式类似于:
Expires Wed, 12 Oct 2016 13:18:42 GMT
Cache-Control
Cache-Control
定义了一组关于缓存的控制字段,常用的有以下几个:
no-cache
指定返回的响应在未经服务器检查其是否被修改之前,不能使用。就是说没有经过服务器确认的缓存是被禁止的,但是经过服务器确认未变更的资源可以避免重复下载。
no-store
这个是简单粗暴地直接禁止浏览器和所有的中继缓存(CDN)储存任何版本的返回响应。
public
响应可以被缓存(默认)。
private
响应可以由浏览器缓存(用户),但是不允许任何中继缓存(CDN)进行缓存。
max-age
指示获取到的响应,从发送请求开始以秒计算,可以重新使用的最长时间间隔。
格式类似于:
Cache-Control private, max-age=31104000
Expires
来自于 HTTP 1.0,Cache-Control
来自于 HTTP 1.1,Cache-Control
优先级高于Expires
。
缓存问题
- 如果 服务器/客户端 时间不准呢?
- 如果在
Expires
/Cache-Control
缓存期间文件发生变化了呢? - 什么时候 no-store 什么时候 private 什么时候........?
解决方案
首先梳理一下,我们需要的是:
- 在过期时间之前缓存可以用
- 在文件有变化的时候客户端立即更新
- 客户端时间不正确也可以正常工作(服务器端时间不正确早就赶紧滚去调了还在写什么笔记)
所以比较好的一个方案是使用 Cache-Control
,并且结合文件版本号。即在文件名中嵌入的一个 指纹。
见上图,将 js 与 css 等静态资源设置 Cache-Control
一年有效期,然后在文件名中加入了一串版本号信息。那么在文件没有变动的时候,浏览器不用发起请求直接可以使用缓存文件;而在文件有变化的时候,由于文件版本号的变更,导致文件名变化,请求的 url 变了,自然文件就更新了。
后记
Last-Modified
与Etag
的方式还是会请求服务器的,只不过返回的是304,对于304的请求 F5 刷新是不会重新下载的。Expires
与Cache-Control
指定 max-age 是直接从本地读取的,不需要请求服务器,他们的 返回状态是 200 OK (BFCache),这个时候 F5 刷新是会重新下载的。
看看浏览器的网络请求:
另外补一张 Cache-Control
策略的图,扒自 Google Developers: